package com.clear.honghefinance.wechat.service.impl;

import com.clear.honghefinance.common.util.redis.RedisUtil;
import com.clear.honghefinance.wechat.config.wechat.WechatConfig;
import com.clear.honghefinance.wechat.constant.WechatApiConstant;
import com.clear.honghefinance.wechat.dto.req.GenerateMpQrcodeDTO;
import com.clear.honghefinance.wechat.dto.resp.AccessTokenVO;
import com.clear.honghefinance.wechat.dto.resp.GenerateMpQrcodeVO;
import com.clear.honghefinance.wechat.service.WechatService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;

import javax.annotation.Resource;
import java.util.Objects;

@Service
@Slf4j
public class WechatServiceImpl implements WechatService {

    @Resource
    private WebClient webClient;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private WechatConfig wechatConfig;

    private final String MP_TOKEN_REDIS_CACHE_KEY = "mp_token_redis_cache_key";


    /**
     * 接口重试次数
     */
    int retry = 3;

    /**
     * 获取微信公众号Access Token
     * 根据appId和secret获取微信公众号的Access Token
     *
     * @param appId  appid 第三方用户唯一凭证
     * @param secret secret 第三方用户唯一凭证密钥，即appsecret
     * @return 微信公众号token
     */
    @Override
    public AccessTokenVO getMpAccessToken(String appId, String secret) {
        // 根据appId和secret构造请求URL
        final String url = String.format(WechatApiConstant.ACCESS_TOKEN_API, appId, secret);
        // 使用webClient发送GET请求，获取Access Token信息
        return webClient.get().uri(url).retrieve().bodyToMono(AccessTokenVO.class)
                // 根据配置的重试策略进行重试
                .retry(retry)
                // 阻塞等待结果
                .block();
    }

    /**
     * 生成微信公众号临时二维码
     *
     * @param token token
     * @param dto   生成微信公众号二维码DTO
     * @return
     */
    @Override
    public GenerateMpQrcodeVO generateMpQrcodeCreate(String token, GenerateMpQrcodeDTO dto) {
        String url = String.format(WechatApiConstant.MP_QRCODE_CREATE, token);
        // 发起POST请求，携带JSON格式的请求体，获取生成的公众号二维码信息
        GenerateMpQrcodeVO vo = webClient.post().uri(url).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(dto))
                .retrieve().bodyToMono(GenerateMpQrcodeVO.class)
                .retry(retry)
                .block();

        if (Objects.isNull(vo) || Strings.isBlank(vo.getTicket())) {
            return vo;  // 返回空对象
        }

        // 通过ticket换取二维码
        vo.setQrCodeUrl(String.format(WechatApiConstant.MP_QRCODE_URL, vo.getTicket()));
        return vo;
    }

    /**
     * 从缓存中获取公众号token
     *
     * @param appid
     * @return
     */
    @Override
    public AccessTokenVO getMpAccessTokenByCache(String appid) {
        String cacheKey = redisUtil.buildKey(MP_TOKEN_REDIS_CACHE_KEY, appid);
        String result = redisUtil.get(cacheKey);

        if (Strings.isBlank(result)){  // 兜底方案：如果缓存为空，意味着xxl-job定时任务可能执行出现问题，手动去拿token
            this.setMpAccessTokenCache(wechatConfig.getMp().getAppId(), wechatConfig.getMp().getSecret());
        }
        return new Gson().fromJson(result, AccessTokenVO.class);
    }

    /**
     * 设置公众号token信息到缓存中
     *
     * @param appid  appid 第三方用户唯一凭证
     * @param secret secret 第三方用户唯一凭证密钥，即appsecret
     */
    @Override
    public void setMpAccessTokenCache(String appid, String secret) {
        // 请求
        AccessTokenVO accessTokenVO = getMpAccessToken(appid, secret);
        // 缓存
        String cacheKey = redisUtil.buildKey(MP_TOKEN_REDIS_CACHE_KEY, appid);
        redisUtil.set(cacheKey, new Gson().toJson(accessTokenVO));
    }
}
